from django.contrib.auth.decorators import login_required
from django.contrib.auth import login as auth_login, logout as auth_logout, \
    authenticate
from django.db.models import Q, Count
from django.shortcuts import render, redirect, get_object_or_404, get_list_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.template import RequestContext
from django.contrib.auth.models import User, Group, Permission
from django.core.exceptions import FieldError
from engonline.models import UserAnswer
from page.models import Page
from test.models import Test
from utils import paginate, calcualte_score

def login(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        
        if user is not None:
            auth_login(request, user)
            return redirect('engonline.views.home')
        else:
            return render(request, 'login.html', {'login_failure': True})
    else:
        return render(request, 'login.html')

def logout(request):
    auth_logout(request)
    return redirect('engonline.views.home')

@login_required
def view_group(request, group, page_number=1):
    """
    Shows details for a single group.
    """
    raise NotImplementedError

@login_required
def list_groups(request):
    """
    Lists all groups (paginated).
    The list is filtered on name if there's a 'search' GET-variable.
    """
    groups_per_page = 10
    
    #Get GET-variables
    page_number = request.GET.get('page')
    filter_string = request.GET.get('search')

    #Init query-components
    query_text = Q()

    if filter_string is not None and len(filter_string) > 0:
        query_text = Q(name__icontains=filter_string)

    #Get all groups with number of members
    try:
        groups = Group.objects.filter(query_text).annotate(number_of_members=Count('user'))
    except FieldError, e:
        groups = Group.objects.filter(query_text) #can't count members, return without
    except Exception, e:
        raise e

    #Paginate and render
    pageinated_groups = paginate(groups, groups_per_page, page_number)
    return render(request, 'list_groups.html', {'groups': pageinated_groups, 'filter_string': filter_string})

@login_required
def view_user(request, username):
    """
    Shows the users info and their test results
    """
    test_results_per_page = 10
    page_number = request.GET.get('page')

    #Get user
    user = get_object_or_404(User, username=username)
    
    #Get all the users question answers (order by date, 1 test session will have the same time timestamp)
    user_answers = UserAnswer.objects.filter(user=user).order_by('-date')
    
    test_results = []
    currentTest = None
    i = 0
    for answer in user_answers:
        if currentTest is None:
            #First iteration, no test yet, append
            test_results.append({'title': answer.test.title, 'correct': 0, 'total': 0, 'date': answer.date})
        elif currentTest != answer.date:
            #New Test, calculate last tests score
            test_results[i]['score'] = calcualte_score(test_results[i]['correct'], test_results[i]['total'])

            #Append new test and increase index
            test_results.append({'title': answer.test.title, 'correct': 0, 'total': 0, 'date': answer.date})
            i += 1

        #Check if correct
        if answer.question.check_answer(answer.answer):
            test_results[i]['correct'] += 1

        #Up total and store current test date
        test_results[i]['total'] += 1
        currentTest = answer.date
    else:
        #If there are answers, calculate the last test as well
        if test_results.__len__() > 0:
            test_results[i]['score'] = calcualte_score(test_results[i]['correct'], test_results[i]['total'])

    #Paginate and render
    pageinated_test_results = paginate(test_results, test_results_per_page, page_number)            

    return render(request, 'view_user.html', {'viewed_user': user, 'test_results': pageinated_test_results})

@login_required
def list_users(request):
    """
    Lists the users (paginated).
    The users are filtered on a search-string and/or a specific group. 
    These conditions are retrieved through GET-variables.
    """
    users_per_page = 10

    #Get GET-variables
    page_number = request.GET.get('page')
    filter_string = request.GET.get('search')
    filter_group = request.GET.get('group')

    #Init query-components
    query_text = Q()
    query_group = Q()

    #If filtering with string
    if filter_string is not None and len(filter_string) > 0:
        query_text = Q(Q(username__icontains=filter_string) | 
                Q(first_name__icontains=filter_string) | 
                Q(last_name__icontains=filter_string))

    #If filtering with group
    if filter_group is not None and len(filter_group) > 0:
        try:
            #Make sure group exists before filtering
            Group.objects.get(name__iexact=filter_group)
            query_group = Q(groups__name__iexact=filter_group)
        except Exception, e:
            pass #Group.object.get failed - do nothing

    #Get users
    users = User.objects.filter(Q(query_text & query_group), is_active=True)

    #Get available groups
    groups = Group.objects.all()

    #Paginate and render
    pageinated_users = paginate(users, users_per_page, page_number)
    return render(request, 'list_users.html', {'users': pageinated_users, 'groups': groups, 'filter_string': filter_string, 'filter_group': filter_group})
    
def create_group(request):
    """
    Creates a group.
    """ 
    required_fields = False
    name_unavailable = False
    
    if request.method == 'POST':
        
        #read forms
        name = request.POST['inputName']
        chosen_permissions = request.POST.getlist('inputPermissions[]')
        
         #check data
        if name == "":
            required_fields = True
        if Group.objects.filter(name=name).exists():
            name_unavailable = True
            
        #if one or more error is detected
        if(required_fields or name_unavailable):
        
                        return render(request,'create_group.html', {'required_fields': required_fields,
                       'name_unavailable': name_unavailable,})
        #create the group
        else:
            group = Group(name=name)
            group.save()
            
            for permission in chosen_permissions:
                group.permissions.add(permission)
            
            group.save()
            request.session['group_saved'] = True
            return HttpResponseRedirect("/auth/edit-group/"+str(group.id)+"/")
    else:
        return render(request, 'create_group.html')
    
def edit_group(request, group_id):
    """
    Edit a group.
    """
    required_fields = False
    name_unavailable = False
    group = get_object_or_404(Group, id=group_id)
    
    if request.method == 'POST':
        
        #read forms
        new_name = request.POST['inputName']
        chosen_permissions = request.POST.getlist('inputPermissions[]')
        
        #check data
        if new_name == "":
            required_fields = True
        if Group.objects.filter(name=new_name).exists() and new_name != group.name:
            name_unavailable = True
            
        #if one or more error is detected
        if(required_fields or name_unavailable):
        
                        return render(request,'create_group.html', {'required_fields': required_fields,
                       'name_unavailable': name_unavailable, 'edit_group':group})
        #edit the group
        else:
            group.name = new_name
            group.permissions.clear()

            for permission in chosen_permissions:
                group.permissions.add(permission)
            
            group.save()
            request.session['group_saved'] = True
            return render(request,'create_group.html', {'group_saved': True, 'edit_group':group})
    else:
        group_saved = request.session.get('group_saved', False)
        request.session['group_saved'] = False
        return render(request,'create_group.html', {'group_saved': group_saved, 'edit_group':group})
    
def delete_group(request, group_id):
    """
    Deletes a group.
    """
    group = get_object_or_404(Group, id=group_id)
    group.delete()
    
    return redirect('auth.views.list_users')

def create_user(request):
    """
    Create a user.
    """
    required_fields = False
    username_unavailable = False
    password_diff = False
    
    if request.method == 'POST':
        
        #read forms
        username = request.POST['inputUsername']
        email = request.POST['inputEmail']
        password = request.POST['inputPassword']
        password2 = request.POST['inputPassword2']
        firstname = request.POST['inputFirstname']
        lastname = request.POST['inputLastname']
        is_staff = request.POST.get('inputStaff', False)
        is_super = request.POST.get('inputSuper', False)
        chosen_groups = request.POST.getlist('inputGroups[]')
        chosen_permissions = request.POST.getlist('inputPermissions[]')
        
         #check data
        if username == "" or password == "" or firstname == "" or lastname == "":
            required_fields = True
        if User.objects.filter(username=username).exists():
            username_unavailable = True
        if password != password2:
            password_diff = True
            
        #if one or more error is detected
        if(required_fields or username_unavailable or password_diff):
        
                        return render(request,'create_user.html', {'required_fields': required_fields,
                       'username_unavailable': username_unavailable, 'password_diff': password_diff})
        #creates the user
        else:
            user = User.objects.create_user(username, email, password)
            user.first_name = firstname
            user.last_name = lastname
            user.is_staff = is_staff
            user.is_superuser = is_super
            
            for group in chosen_groups:
                user.groups.add(group)
                
            for permission in chosen_permissions:
                user.user_permissions.add(permission)
            
            user.save()
            return redirect('auth.views.list_users')
    
    else:
        return render(request, 'create_user.html')
    
def edit_user(request, username):
    """
    Edit user.
    """
    user = get_object_or_404(User, username=username)    
    
    required_fields = False
    password_diff = False
    
    if request.method == 'POST':
        
        password = request.POST['inputPassword']
        password2 = request.POST['inputPassword2']
        firstname = request.POST['inputFirstname']
        lastname = request.POST['inputLastname']
        
        #check data
        if username == ""  or firstname == "" or lastname == "":
            required_fields = True
        if password != password2:
            password_diff = True
        
        if(required_fields or password_diff):
        
            return render(request,'create_user.html', {'edit_user': user, 'required_fields': required_fields,
            'password_diff': password_diff})
        else:
            if password != "":
                user.set_password(password)
                user.save()
            
            set_attributes(user, request) 
            return redirect('auth.views.list_users')  

    else:
        return render(request, 'create_user.html',{'edit_user': user})
    
def set_attributes(user, request):
    """
    Sets attributes on user.
    """
    chosen_groups = request.POST.getlist('inputGroups[]')
    chosen_permissions = request.POST.getlist('inputPermissions[]')
    
    user.first_name = request.POST['inputFirstname']
    user.last_name = request.POST['inputLastname']
    user.email = request.POST['inputEmail']
    user.is_staff = request.POST.get('inputStaff', False)
    user.is_superuser = request.POST.get('inputSuper', False)
    
    user.groups.clear()
    
    for group in chosen_groups:
            user.groups.add(group)
        
    user.user_permissions.clear()

    for permission in chosen_permissions:
        user.user_permissions.add(permission)
    
    user.save()

def trash_user(request, username):
    """
    Trashs user.
    """
    user = get_object_or_404(User, username=username)
    user.is_active = False
    user.save()
    return redirect('auth.views.list_users')

def delete_user(request, username):
    """
    Deletes user if it is not the creator or changer of any page.
    """
    user = get_object_or_404(User, username=username)
    #Delete all answers
    UserAnswer.objects.filter(user=user).delete()
    #if the user has created any pages, set active to false
    if Page.objects.filter(creator=user).exists() or Page.objects.filter(changer=user).exists():
        user.is_active = False
        user.save()
    else:
        user.delete()
    
    return redirect('auth.views.list_users')
